﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace IISLogAnalyzer
{
    public partial class FormIISLogAnalyzer : Form
    {
        bool isInitial = true;

        DateTime dtTimeStart = DateTime.Now;
        DateTime dtTimeEnd = DateTime.Now;

        public FormIISLogAnalyzer()
        {
            InitializeComponent();

            BindWebSites();

            comboBox1.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;

            isInitial = false;

            BindDate();
        }

        private void btnAnalyzer_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
            {
                folderBrowserDialog.Description = "请选择一个存放IIS日志的目录";
                folderBrowserDialog.RootFolder = Environment.SpecialFolder.MyComputer; // 设置起始文件夹  

                // 显示文件夹选择对话框  
                if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
                {
                    // 用户选择了文件夹  
                    string selectedFolder = folderBrowserDialog.SelectedPath;

                    TraverseDirectoryFiles.TraverseDirectory(selectedFolder);

                    List<string> files = TraverseDirectoryFiles.filePaths;

                    if (files.Count == 0)
                    {
                        MessageBox.Show("当前目录没有找到IIS日志！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }
                    else
                    {
                        FormProgress formProgress = new FormProgress(this);
                        formProgress.ShowDialog();

                    }
                }
            }
        }

        static int CountLinesAsync(string filePath)
        {
            int count = 0;
            using (StreamReader file = new StreamReader(filePath))
            {
                string line;
                while ((line = file.ReadLine()) != null)
                {
                    count++;
                }
            }
            return count;
        }

        /// <summary>
        /// 分析并记录数据到Access
        /// </summary>
        /// <param name="logFilePath">单个IIS日志绝对路径</param>
        public void AnalyzeLog(string logFilePath, ProgressBar progressBar = null, Label progressText = null)
        {
            string logTags = " "; // 日志空格
            Dictionary<string, string> logsTitles = new Dictionary<string, string>();
            logsTitles.Add("date", "(\\d{4}-\\d{2}-\\d{2})");//日期：日志条目的日期。
            logsTitles.Add("time", "(\\d{2}:\\d{2}:\\d{2})");//时间：日志条目的具体时间。
            logsTitles.Add("s-sitename", "(\\S+)");//服务名称：IIS服务器的名称。
            logsTitles.Add("s-computername", "(\\S+)");//服务器名称：运行IIS的计算机的主机名。
            logsTitles.Add("s-ip", "(\\S+)");//服务器IP地址：运行IIS的计算机的IP地址。
            logsTitles.Add("cs-method", "(\\S+)");//方法：HTTP请求的方法，如GET、POST等。
            logsTitles.Add("cs-uri-stem", "(\\S+)");//URI资源：请求的完整URL路径（不包括查询字符串）。
            logsTitles.Add("cs-uri-query", "(\\S+)");//URI查询：URL中的查询字符串部分。
            logsTitles.Add("s-port", "(\\S+)");//服务器端口号：IIS服务器监听的端口号。
            logsTitles.Add("cs-username", "(\\S+)");//用户名：用于认证的用户名（如果有的话）。
            logsTitles.Add("c-ip", "(\\S+)");//客户端IP地址：发出请求的客户端的IP地址。
            logsTitles.Add("cs-version", "(\\S+)");//协议版本：使用的HTTP版本，如HTTP/1.1。
            logsTitles.Add("cs(User-Agent)", "(\\S+)");//用户代理：发出请求的客户端的用户代理字符串，通常包含浏览器类型、版本和其他相关信息。
            logsTitles.Add("cs(Cookie)", "(\\S+)");//Cookie：发送到服务器的cookie（如果存在的话）。
            logsTitles.Add("cs(Referer)", "(\\S+)");//引用网站：请求中包含的Referer头部，指示原始网页的URL（如果存在的话）。
            logsTitles.Add("cs-host", "(\\S+)");//主机：请求的主机名部分（有时与URI资源相同）。
            logsTitles.Add("sc-status", "(\\d+)");//协议状态：HTTP响应的状态码，如200、404等。
            logsTitles.Add("sc-substatus", "(\\d+)");//协议子状态：HTTP响应的子状态码（某些服务器可能会记录此字段）。
            logsTitles.Add("sc-win32-status", "(\\d+)");//Win32状态：与Windows网络操作相关的状态码（非所有服务器都记录此字段）。
            logsTitles.Add("sc-bytes", "(\\d+)");//发送字节数：服务器发送给客户端的字节数。
            logsTitles.Add("cs-bytes", "(\\d+)");//接收字节数：客户端发送给服务器的字节数。
            logsTitles.Add("time-taken", "(\\d+)");//所用时间：完成此请求所需的总时间。
            Dictionary<int, string> logsValues = new Dictionary<int, string>();

            string pattern = "";

            var logData = new Dictionary<string, int>(); // 存储每个网站的访问和流量数据  
            string line;
            using (StreamReader file = new StreamReader(logFilePath))
            {
                //************这两句话是初始化表************
                //DbHelperOleDb.ExecuteSql("delete from IISLogAnalyzer;");
                //DbHelperOleDb.ExecuteSql("alter table IISLogAnalyzer alter id counter(1,1);");
                //************这两句话是初始化表************

                int lineCount = CountLinesAsync(logFilePath);

                if (progressBar != null && progressText != null)
                {
                    ControlProxyHelper.SetValue(progressBar, "Minimum", 0);
                    ControlProxyHelper.SetValue(progressBar, "Maximum", 100);
                    ControlProxyHelper.SetValue(progressText, "Text", "0% - 0/"+ lineCount);
                }

                int lineIdex = 1;

                while ((line = file.ReadLine()) != null)
                {
                    if (line.StartsWith("#Fields:"))
                    {
                        logsValues = new Dictionary<int, string>();
                        string[] titles = line.Replace("#Fields:", "").Split(' ');
                        int i = 0;
                        foreach (string title in titles)
                        {
                            if (logsTitles.ContainsKey(title))
                            {
                                logsValues.Add(i, title);
                                pattern += logsTitles[title] + logTags;
                                i++;
                            }
                        }
                    }

                    if (pattern.Length > 0)
                    {
                        if (pattern.EndsWith(logTags))
                        {
                            pattern = pattern.Substring(0, pattern.Length - 1);
                        }
                    }
                    else
                    {
                        continue;
                    }

                    // 使用正则表达式匹配日志行格式  
                    Match match = Regex.Match(line, @pattern);
                    if (match.Success)
                    {
                        string insertSql = "insert into IISLogAnalyzer({columns}) values({values})";
                        string columns = "";
                        string values = "";
                        for (int i = 1; i < match.Groups.Count; i++)
                        {
                            columns += "[_" + logsValues[i - 1] + "],";
                            values += "'" + match.Groups[i].Value + "',";
                        }

                        columns += "_add_time";
                        values += "'" + DateTime.Now + "'";

                        try
                        {
                            if (progressBar != null && progressText != null)
                            {
                                int gress = (int)((double)lineIdex / lineCount * 100);
                                ControlProxyHelper.SetValue(progressBar, "Value", gress);                                
                                ControlProxyHelper.SetValue(progressText, "Text", gress+"% - " + lineIdex + "/" + lineCount);
                            }

                            DbHelperOleDb.ExecuteSql(insertSql.Replace("{columns}", columns).Replace("{values}", values));

                            lineIdex++;
                        }
                        catch (Exception e)
                        {
                            Logger.WriteLog(e.Message);
                        }
                    }
                }
            }
        }

        private void btnColums_Click(object sender, EventArgs e)
        {
            FormSetColumns fsc = new FormSetColumns();
            fsc.ShowDialog();
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }

        /// <summary>
        /// 根据字节数，返回带相应单位的字符串。
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        public string GetByteUnite(double v)
        {
            if (v < 1024) // 通常我们使用1024作为KB的界限，而不是1000  
            {
                return v.ToString("F2") + " b";
            }
            else if (v < 1024 * 1024) // 1MB = 1024KB  
            {
                return (v / 1024d).ToString("F2") + " KB"; // 注意单位是大写的KB  
            }
            else if (v < 1024 * 1024 * 1024) // 1GB = 1024MB  
            {
                return (v / (1024d * 1024d)).ToString("F2") + " MB";
            }
            else if (v < 1024L * 1024L * 1024L * 1024L) // 1TB = 1024GB，这里使用long来确保计算不会溢出  
            {
                return (v / (1024d * 1024d * 1024d)).ToString("F2") + " GB";
            }
            else
            {
                return (v / (1024d * 1024d * 1024d * 1024d)).ToString("F2") + " TB";
            }
        }

        /// <summary>
        /// 绑定数据
        /// </summary>
        public void BindDate()
        {
            if (isInitial)
            {
                return;
            }

            ControlProxyHelper.SetValue(dgvList, "DataSource", null);

            string sqlWhere = "[_date] between '" + dtpDateStart.Value.ToString("yyyy-MM-dd") + "' and '" + dtpDateEnd.Value.ToString("yyyy-MM-dd") + "' and [_time] between '00:00:00' and '59:59:59'";

            if (int.Parse(ControlProxyHelper.GetValue(cbxWebSites, "SelectedIndex").ToString()) > 0)
            {
                sqlWhere += " and [_s-sitename] = '" + ControlProxyHelper.GetValue(cbxWebSites, "Text").ToString() + "'";
            }

            double sd = Convert.ToDouble(DbHelperOleDb.GetSingle(("SELECT Sum([_sc-bytes]) AS c FROM IISLogAnalyzer where +" + sqlWhere + "; ")));
            double ad = Convert.ToDouble(DbHelperOleDb.GetSingle(("SELECT Sum([_cs-bytes]) AS c FROM IISLogAnalyzer where +" + sqlWhere + "; ")));

            ControlProxyHelper.SetValue(lbSend, "Text", GetByteUnite(sd));
            ControlProxyHelper.SetValue(lbAccepte, "Text", GetByteUnite(ad));

            string Ips = Convert.ToInt32(DbHelperOleDb.GetSingle(("select count(1) from ( select distinct [_c-ip] as ip FROM IISLogAnalyzer where +" + sqlWhere + ") as tab;"))).ToString();

            ControlProxyHelper.SetValue(lbIps, "Text", Ips);

            string strSql = "select ID," + Statics.GetSqlColumns() + " from IISLogAnalyzer where " + sqlWhere + "";

            if (ControlProxyHelper.GetValue(ucPageManager1, "uc_search").ToString().Trim() != "")
            {
                strSql += " and " + Statics.GetLikeSqlColumns(ControlProxyHelper.GetValue(ucPageManager1, "uc_search").ToString().Trim());
            }

            int recordCount = Convert.ToInt32(DbHelperOleDb.GetSingle(PagingHelper.CreateCountingSql(strSql)));

            ControlProxyHelper.SetValue(ucPageManager1, "uc_setPageDescription", recordCount);

            string orderby = "";

            if (int.Parse(ControlProxyHelper.GetValue(comboBox1, "SelectedIndex").ToString()) > 0)
            {
                if (int.Parse(ControlProxyHelper.GetValue(comboBox1, "SelectedIndex").ToString()) == 1)
                {
                    orderby = " VAL([发送字节数])";
                }
                else
                {
                    orderby = " VAL([发送字节数]) desc";
                }
            }

            if (orderby == "")
            {
                if (int.Parse(ControlProxyHelper.GetValue(comboBox2, "SelectedIndex").ToString()) > 0)
                {
                    if (int.Parse(ControlProxyHelper.GetValue(comboBox2, "SelectedIndex").ToString()) == 1)
                    {
                        orderby = " VAL([接收字节数])";
                    }
                    else
                    {
                        orderby = " VAL([接收字节数]) desc";
                    }
                }
            }
            else
            {
                if (int.Parse(ControlProxyHelper.GetValue(comboBox2, "SelectedIndex").ToString()) > 0)
                {
                    if (int.Parse(ControlProxyHelper.GetValue(comboBox2, "SelectedIndex").ToString()) == 1)
                    {
                        orderby += ",VAL([接收字节数])";
                    }
                    else
                    {
                        orderby += ",VAL([接收字节数]) desc";
                    }
                }
            }

            if (orderby == "")
            {
                if (int.Parse(ControlProxyHelper.GetValue(comboBox3, "SelectedIndex").ToString()) > 0)
                {
                    if (int.Parse(ControlProxyHelper.GetValue(comboBox3, "SelectedIndex").ToString()) == 1)
                    {
                        orderby = "VAL([耗时（ms）])";
                    }
                    else
                    {
                        orderby = "VAL([耗时（ms）]) desc";
                    }
                }
            }
            else
            {
                if (int.Parse(ControlProxyHelper.GetValue(comboBox3, "SelectedIndex").ToString()) > 0)
                {
                    if (int.Parse(ControlProxyHelper.GetValue(comboBox3, "SelectedIndex").ToString()) == 1)
                    {
                        orderby += ",VAL([耗时（ms）])";
                    }
                    else
                    {
                        orderby += ",VAL([耗时（ms）]) desc";
                    }
                }
            }

            DataSet ds = DbHelperOleDb.Query(PagingHelper.CreatePagingSql(recordCount, int.Parse(ControlProxyHelper.GetValue(ucPageManager1, "uc_pageSize").ToString()), int.Parse(ControlProxyHelper.GetValue(ucPageManager1, "uc_pageIndex").ToString()), strSql, (orderby == "" ? "id desc" : orderby + ",id desc")));

            if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
            {
                foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    dr["发送字节数"] = GetByteUnite(Convert.ToDouble(dr["发送字节数"].ToString()));
                }
                ControlProxyHelper.SetValue(dgvList, "DataSource", ds.Tables[0]);
            }
        }

        private void cbxWebSites_SelectedIndexChanged(object sender, EventArgs e)
        {
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }
        /// <summary>
        /// 绑定网站
        /// </summary>
        public void BindWebSites()
        {
            DataSet ds = DbHelperOleDb.Query("SELECT DISTINCT [_s-sitename] as title from IISlogAnalyzer;");

            ControlProxyHelper.InvokeControlMethod(this, "cbxWebSites", "Items", "Clear");

            if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
            {
                DataTable dt = new DataTable();
                dt.Columns.Add("index");
                dt.Columns.Add("title");

                DataRow dr = dt.NewRow();
                dr["index"] = "0";
                dr["title"] = "所有服务名称";

                dt.Rows.Add(dr);

                for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                {
                    dr = dt.NewRow();
                    dr["index"] = (i + 1).ToString();
                    dr["title"] = ds.Tables[0].Rows[i]["title"].ToString();
                    dt.Rows.Add(dr);
                }

                ControlProxyHelper.SetProperty(this, "cbxWebSites", "DisplayMember", "title");
                ControlProxyHelper.SetProperty(this, "cbxWebSites", "ValueMember", "index");
                ControlProxyHelper.SetProperty(this, "cbxWebSites", "DataSource", dt);

            }

            ControlProxyHelper.SetProperty(this, "cbxWebSites", "SelectedIndex", 0);

            ds = DbHelperOleDb.Query("SELECT max([_date]) as endDate,min([_date]) as startDate from IISlogAnalyzer;");
            if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
            {
                ControlProxyHelper.SetStatusStripText(statusStrip1, 0, "当前数据库中日志的发生日期区间：【" + ds.Tables[0].Rows[0]["startDate"] + "】~【" + ds.Tables[0].Rows[0]["endDate"] + "】");
            }

        }

        private void dgvList_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            foreach (DataGridViewColumn dgvc in dgvList.Columns)
            {
                dgvc.SortMode = DataGridViewColumnSortMode.NotSortable;

                if (dgvc.HeaderText == "URI资源")
                {
                    dgvc.AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
                }
                else
                {
                    dgvc.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                }
            }
        }

        private void dtpDateStart_ValueChanged(object sender, EventArgs e)
        {
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }

        private void dtpDateEnd_ValueChanged(object sender, EventArgs e)
        {
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }

        private void comboBox3_SelectedIndexChanged(object sender, EventArgs e)
        {
            ControlProxyHelper.SetValue(ucPageManager1, "uc_pageIndex", 1);
            BindDate();
        }

        private void btnChart_Click(object sender, EventArgs e)
        {
            FormCharts formCharts = new FormCharts();
            formCharts.ShowDialog();
        }
    }
}
